#include <pmemory.h>

static uint8_t memory[PMEM_SIZE];

bool in_mem(uint64_t addr);
uint64_t mmio_read(uint64_t addr, size_t len);
void mmio_write(uint64_t addr, size_t len, uint64_t data);

//use by vsrc
void read_mem(long long vaddr, int len, long long *rdata) {
  // printf("read: 0x%llx\n", vaddr);
  if(in_mem(vaddr)) {
    uint8_t* paddr = vaddr_to_paddr(vaddr);
    *rdata = host_read(paddr, len);
  }
  else {
    *rdata = mmio_read(vaddr, len);
  }
}

void write_mem(long long vaddr, int len, long long wdata) {
  // printf("write to: 0x%llx:", vaddr);
  // printf("0x%llx\n", wdata);
  if(in_mem(vaddr)) {
    uint8_t* paddr = vaddr_to_paddr(vaddr);
    host_write(paddr, len, wdata);
  }
  else {
    mmio_write(vaddr, len, wdata);
  }
}

//use by csrc
uint64_t vaddr_read(uint64_t vaddr, int len) {
  uint8_t* paddr = vaddr_to_paddr(vaddr);
  switch (len) {
  case 4:
    return (uint64_t)(*(uint32_t *)paddr);
    break;
  default:
    return *(uint64_t *)paddr;
  }
}

void init_mem() {
  uint32_t *p = (uint32_t *)memory;
  int i;
  for (i = 0; i < (int)(PMEM_SIZE / sizeof(p[0])); i++) {
    p[i] = rand();
  }
  printf("init mem complete, from 0x%08lx to 0x%08lx\n", PMEM_LEFT, PMEM_RIGHT);
}

bool in_mem(uint64_t addr) {
  if (addr < PMEM_LEFT || addr > PMEM_RIGHT)
    return false;
  return true;
}

void out_of_bound(uint64_t addr) {
  printf("addr: 0x%lx out of memory bound from 0x%lx to 0x%lx\n", addr, PMEM_LEFT, PMEM_RIGHT);
  assert(0);
}

uint8_t* vaddr_to_paddr(uint64_t addr) {
  if(in_mem(addr))
    return memory + addr - PMEM_BASE;
  out_of_bound(addr);
  return NULL;
}

uint8_t* guest_to_host(uint64_t paddr) { return memory + paddr - PMEM_BASE; }
uint64_t host_to_guest(uint8_t *haddr) { return haddr - memory + PMEM_BASE; }
